package com.myBlast.client;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import com.extjs.gxt.ui.client.Style.Scroll;
import com.extjs.gxt.ui.client.data.BaseListLoader;
import com.extjs.gxt.ui.client.data.BeanModel;
import com.extjs.gxt.ui.client.data.BeanModelReader;
import com.extjs.gxt.ui.client.data.ListLoadResult;
import com.extjs.gxt.ui.client.data.ListLoader;
import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.data.RpcProxy;
import com.extjs.gxt.ui.client.event.ButtonEvent;
import com.extjs.gxt.ui.client.event.Events;
import com.extjs.gxt.ui.client.event.GridEvent;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.event.MenuEvent;
import com.extjs.gxt.ui.client.event.SelectionListener;
import com.extjs.gxt.ui.client.event.WindowEvent;
import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.store.Store;
import com.extjs.gxt.ui.client.store.StoreEvent;
import com.extjs.gxt.ui.client.widget.BoxComponent;
import com.extjs.gxt.ui.client.widget.Component;
import com.extjs.gxt.ui.client.widget.ContentPanel;
import com.extjs.gxt.ui.client.widget.Dialog;
import com.extjs.gxt.ui.client.widget.Info;
import com.extjs.gxt.ui.client.widget.button.Button;
import com.extjs.gxt.ui.client.widget.custom.Portlet;
import com.extjs.gxt.ui.client.widget.form.StoreFilterField;
import com.extjs.gxt.ui.client.widget.grid.CheckBoxSelectionModel;
import com.extjs.gxt.ui.client.widget.grid.ColumnConfig;
import com.extjs.gxt.ui.client.widget.grid.ColumnData;
import com.extjs.gxt.ui.client.widget.grid.ColumnModel;
import com.extjs.gxt.ui.client.widget.grid.Grid;
import com.extjs.gxt.ui.client.widget.grid.GridCellRenderer;
import com.extjs.gxt.ui.client.widget.grid.RowNumberer;
import com.extjs.gxt.ui.client.widget.menu.CheckMenuItem;
import com.extjs.gxt.ui.client.widget.menu.Menu;
import com.extjs.gxt.ui.client.widget.menu.MenuItem;
import com.extjs.gxt.ui.client.widget.toolbar.LabelToolItem;
import com.extjs.gxt.ui.client.widget.toolbar.SeparatorToolItem;
import com.extjs.gxt.ui.client.widget.toolbar.ToolBar;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.myBlast.client.icons.ExampleIcons;
import com.myBlast.client.models.FormModel;
import com.myBlast.data.BlastResult;

public class BlastGrid {

	private Grid<BeanModel> grid = null;
	private StoreFilterField<BeanModel> filterField = null;
	
	private int id = 0;
	
	public BlastGrid(int count)
	{
		id = count;
	}
	
	public StoreFilterField<BeanModel> getfilterField()
	{
		return filterField;
	}
	
	public String getId()
	{
		return String.valueOf(id);
	}
	
	public void setGridProjectName(String name)
	{
		grid.setData("projectName", name);
	}
	
	@SuppressWarnings("unchecked")
	public void configureGrid()
	{
		RpcProxy<List<BlastResult>> proxy = new RpcProxy<List<BlastResult>>() {
			@Override
			public void load(Object loadConfig, AsyncCallback<List<BlastResult>> callback) {
			}
		};
		BeanModelReader reader = new BeanModelReader();

		// loader and store
		ListLoader<ListLoadResult<ModelData>> loader = new BaseListLoader<ListLoadResult<ModelData>>(proxy, reader);
		ListStore<BeanModel> gridStore = new ListStore<BeanModel>(loader);

		RowNumberer r = new RowNumberer();
		r.setWidth(30);
		List<ColumnConfig> columns = new ArrayList<ColumnConfig>();
		columns.add(r);
		
		CheckBoxSelectionModel sm = new CheckBoxSelectionModel();
		columns.add(sm.getColumn());

		ColumnConfig column = new ColumnConfig("organismName","HitName",127);
		column.setToolTip("HitName");
		columns.add(column);

		column = new ColumnConfig("organismId","TaxId",60);
		column.setToolTip("Taxonomy Id");
		columns.add(column);

		column = new ColumnConfig("maxEVal","Max E-Val",60);
		column.setToolTip("Max E-Val");
		columns.add(column);

		column = new ColumnConfig("minEVal","Min E-Val",60);
		column.setToolTip("Min E-Val");
		columns.add(column);

		column = new ColumnConfig("maxBitScore","Max Bit Score",60);
		column.setToolTip("Max Bit Score");
		columns.add(column);

		column = new ColumnConfig("minBitScore","Min Bit Score",60);
		column.setToolTip("Min Bit Score");
		columns.add(column);

		column = new ColumnConfig("id","Blast File", 60);
		column.setToolTip("Blast File");
		column.setRenderer(getRenderer());
		columns.add(column);
		ColumnModel cm = new ColumnModel(columns);

		gridStore.addListener(Store.DataChanged, new Listener<StoreEvent<ModelData>>() {
			@Override
			public void handleEvent(StoreEvent<ModelData> be) {
				WidgetUtilities.hideLoading();
			}
		});
		grid = new Grid<BeanModel>(gridStore, cm);
		grid.setSelectionModel(sm);
		grid.setStyleAttribute("borderTop", "none");
		grid.addPlugin(sm);
		grid.setBorders(true);
		grid.setLoadMask(true);
		grid.setAutoHeight(true);
		grid.getView().setForceFit(true);
	}

	public Grid<BeanModel> getGrid()
	{
		return grid;
	}

	public ToolBar createToolBar() {
		ToolBar toolBar = new ToolBar();
		Button removeButton = new Button("Remove", ((ExampleIcons) GWT.create(ExampleIcons.class)).delete(), 
				addRemoveButtonSelectionListener());
		removeButton.setItemId("removeButton_"+getId());
		toolBar.add(removeButton);
		toolBar.add(new SeparatorToolItem());
		LabelToolItem labelToolItem = new LabelToolItem("Filter:");
		toolBar.add(labelToolItem);
		createStoreFilterForGrid();
		toolBar.add(filterField);  
		toolBar.add(new SeparatorToolItem());
		toolBar.add(createScollerButton());
		toolBar.add(createToolsButton());
		Button saveButton = new Button("Save Project", addSaveButtonSelectionListener());
		saveButton.setItemId("saveButton_"+getId());
		toolBar.add(saveButton);
		return toolBar;
	}

	private Button createScollerButton() {
		Menu scrollMenu = new Menu();
		scrollMenu.setMaxHeight(200);
		CheckMenuItem menuItem = new CheckMenuItem("By Name"); 
		menuItem.setChecked(true);
		menuItem.setTitle("organismName");
		menuItem.setGroup("props");
		menuItem.addSelectionListener(addScollerMenuSelectionListener());
		scrollMenu.add(menuItem);
		menuItem = new CheckMenuItem("By Taxid");
		menuItem.setTitle("organismId");
		menuItem.setChecked(false);
		menuItem.setGroup("props");
		menuItem.addSelectionListener(addScollerMenuSelectionListener());
		scrollMenu.add(menuItem);
		Button scrollerButton = new Button("Property to search by"); 
		scrollMenu.setItemId("scrollMenu_"+getId());
		scrollerButton.setMenu(scrollMenu);
		return scrollerButton;
	}

	private SelectionListener<? extends MenuEvent> addScollerMenuSelectionListener() {
		return new SelectionListener<MenuEvent>() {
			@Override
			public void componentSelected(MenuEvent ce) {
				Menu menu = (Menu)ce.getSource();
				Iterator<Component> iterator = menu.iterator();
				while(iterator.hasNext())
				{
					CheckMenuItem checkMenuItem = (CheckMenuItem) iterator.next();
					if (checkMenuItem.isChecked())
					{
						String id = menu.getItemId().split("_")[1];
						BlastGrid blastGrid = MyPortal.map.get(id); 
						StoreFilterField<BeanModel> filter = blastGrid.getfilterField();
						filter.setProperty(checkMenuItem.getTitle());
						break;
					}
				}
			}
		};
	}

	private Button createToolsButton() {
		Menu toolsMenu = new Menu();
		toolsMenu.setMaxHeight(200);
		MenuItem item = new MenuItem("Select Organisms", new SelectionListener<MenuEvent>() {
			@Override
			public void componentSelected(MenuEvent ce) {
				WidgetUtilities.displayLoading();
				Menu menu = (Menu)ce.getSource();
				String id = menu.getItemId().split("_")[1];
				BlastGrid blastGrid = MyPortal.map.get(id);
				Grid<BeanModel> grid = blastGrid.getGrid();
				MyPortal.reselectOrg(grid.getStore().getModels());
				WidgetUtilities.hideLoading();
			}
		});
//		item.setItemId("reselectMenuItem_" + getId());
		toolsMenu.setItemId("reselectMenuItem_" + getId());
		toolsMenu.add(item);
		item = new MenuItem("Download CSV", createDownloadCSVSelectionListener());
		toolsMenu.add(item);
		item = new MenuItem("Download XLS", createDownloadXLSSelectionListener());
		toolsMenu.add(item);
		item = new MenuItem("Download Project", createDownloadProjectSelectionListener());
		toolsMenu.add(item);
		Button toolsButton = new Button("Tools"); 
		toolsButton.setMenu(toolsMenu);
		return toolsButton;
	}

	private SelectionListener<? extends MenuEvent> createDownloadProjectSelectionListener() {
		return new SelectionListener<MenuEvent>() {
			@Override
			public void componentSelected(MenuEvent ce) {
				Menu menu = (Menu)ce.getSource();
				String id = menu.getItemId().split("_")[1];
				BlastGrid blastGrid = MyPortal.map.get(id);
				Grid<BeanModel> grid = blastGrid.getGrid();
				final List<BeanModel> models = grid.getStore().getModels();
				if (models.size() == 0)
				{
					Info.display("Error", "No data to download");
				}
				else
				{
					if (MyPortal.projectUnsaved)
					{
						Dialog dialog = WidgetUtilities.makeDialog("Download Warning!","Do you wish to save the project too?");
						dialog.show();
						dialog.addListener(Events.Hide, new Listener<WindowEvent>() {
							public void handleEvent(WindowEvent be) {
								Dialog d = (Dialog)be.getSource();
								AsyncCallback<String> callback = new AsyncCallback<String>() {
									@Override
									public void onSuccess(String result) {
										Info.display("File Name", result);
										if (result.endsWith(".prg"))
											WidgetUtilities.addDownloadiframe(result,MyPortal.txtField.getValue());
										else Info.display("Error", result);
									}
									@Override
									public void onFailure(Throwable caught) {
										Info.display("Error", "Could not download project");
									}
								};
								if (be.getButtonClicked() == d.getButtonById("yes"))
								{
									download(true,models,callback,".prg");
									MyPortal.projectUnsaved = false;
								}
								else if (be.getButtonClicked() == d.getButtonById("no"))
								{
									download(false,models,callback,".prg");
									MyPortal.projectUnsaved = true;
								}
								else if (be.getButtonClicked() == d.getButtonById("cancel"))
								{
									Info.display("cancel","do nothing");
								}
							}
						});
					}
					else
					{
						AsyncCallback<String> callback = new AsyncCallback<String>() {
							@Override
							public void onSuccess(String result) {
								Info.display("File Name", result);
								if (result.endsWith(".prg"))
									WidgetUtilities.addDownloadiframe(result, MyPortal.txtField.getValue());
								else Info.display("Error", result);
							}
							@Override
							public void onFailure(Throwable caught) {
								Info.display("Error", "Could not download project");
							}
						};
						download(false,models,callback,".prg");
					}
				}
			}
		};
	}

	private SelectionListener<MenuEvent> createDownloadCSVSelectionListener() {
		return new SelectionListener<MenuEvent>() {
			@Override
			public void componentSelected(MenuEvent ce) {
				Menu menu = (Menu)ce.getSource();
				String id = menu.getItemId().split("_")[1];
				BlastGrid blastGrid = MyPortal.map.get(id);
				Grid<BeanModel> grid = blastGrid.getGrid();
				final List<BeanModel> models = grid.getStore().getModels();
				if (models.size() == 0)
				{
					Info.display("Error", "No data to download");
				}
				else
				{
					if (MyPortal.projectUnsaved)
					{
						Dialog dialog = WidgetUtilities.makeDialog("Download Warning!","Do you wish to save the project too?");
						dialog.show();
						dialog.addListener(Events.Hide, new Listener<WindowEvent>() {
							public void handleEvent(WindowEvent be) {
								Dialog d = (Dialog)be.getSource();
								AsyncCallback<String> callback = new AsyncCallback<String>() {
									@Override
									public void onSuccess(String result) {
										Info.display("File Name", result);
										if (result.endsWith(".csv"))
											WidgetUtilities.addDownloadiframe(result,MyPortal.txtField.getValue());
										else Info.display("Error", result);
									}
									@Override
									public void onFailure(Throwable caught) {
										Info.display("Error", "Could not download project");
									}
								};
								if (be.getButtonClicked() == d.getButtonById("yes"))
								{
									download(true,models,callback,".csv");
									MyPortal.projectUnsaved = false;
								}
								else if (be.getButtonClicked() == d.getButtonById("no"))
								{
									download(false,models,callback,".csv");
									MyPortal.projectUnsaved = true;
								}
								else if (be.getButtonClicked() == d.getButtonById("cancel"))
								{
									Info.display("cancel","do nothing");
								}
							}
						});
					}
					else
					{
						AsyncCallback<String> callback = new AsyncCallback<String>() {
							@Override
							public void onSuccess(String result) {
								Info.display("File Name", result);
								if (result.endsWith(".csv"))
									WidgetUtilities.addDownloadiframe(result,MyPortal.txtField.getValue());
								else Info.display("Error", result);
							}
							@Override
							public void onFailure(Throwable caught) {
								Info.display("Error", "Could not download project");
							}
						};
						download(false,models,callback,".csv");
					}
				}
			}
		};
	}
	
	private SelectionListener<MenuEvent> createDownloadXLSSelectionListener() {
		return new SelectionListener<MenuEvent>() {
			@Override
			public void componentSelected(MenuEvent ce) {
				Menu menu = (Menu)ce.getSource();
				String id = menu.getItemId().split("_")[1];
				BlastGrid blastGrid = MyPortal.map.get(id);
				Grid<BeanModel> grid = blastGrid.getGrid();
				final List<BeanModel> models = grid.getStore().getModels();
				if (models.size() == 0)
				{
					Info.display("Error", "No data to download");
				}
				else
				{
					if (MyPortal.projectUnsaved)
					{
						Dialog dialog = WidgetUtilities.makeDialog("Download Warning!","Do you wish to save the project too?");
						dialog.show();
						dialog.addListener(Events.Hide, new Listener<WindowEvent>() {
							public void handleEvent(WindowEvent be) {
								Dialog d = (Dialog)be.getSource();
								AsyncCallback<String> callback = new AsyncCallback<String>() {
									@Override
									public void onSuccess(String result) {
										Info.display("File Name", result);
										if (result.endsWith(".xls"))
											WidgetUtilities.addDownloadiframe(result,MyPortal.txtField.getValue());
										else Info.display("Error", result);
									}
									@Override
									public void onFailure(Throwable caught) {
										Info.display("Error", "Could not download xls file");
									}
								};
								if (be.getButtonClicked() == d.getButtonById("yes"))
								{
									download(true,models,callback,".xls");
									MyPortal.projectUnsaved = false;
								}
								else if (be.getButtonClicked() == d.getButtonById("no"))
								{
									download(false,models,callback,".xls");
									MyPortal.projectUnsaved = true;
								}
								else if (be.getButtonClicked() == d.getButtonById("cancel"))
								{
									Info.display("cancel","do nothing");
								}
							}
						});
					}
					else
					{
						AsyncCallback<String> callback = new AsyncCallback<String>() {
							@Override
							public void onSuccess(String result) {
								Info.display("File Name", result);
								if (result.endsWith(".xls"))
									WidgetUtilities.addDownloadiframe(result,MyPortal.txtField.getValue());
								else Info.display("Error", result);
							}
							@Override
							public void onFailure(Throwable caught) {
								Info.display("Error", "Could not download project");
							}
						};
						download(false,models,callback,".xls");
					}
				}
			}
		};
	}
	
	private void createStoreFilterForGrid() {
		filterField = new StoreFilterField<BeanModel>() {  
			@Override
			protected boolean doSelect(Store<BeanModel> store,
					BeanModel parent, BeanModel record, String property,
					String filter) {
				if (property.equals("organismName"))
				{
					name = record.get(property);
					name = name.toLowerCase();  
					if (name.contains(filter.toLowerCase())) {  
						return true;  
					}
				}
				else if (property.equals("organismId"))
				{
					name = String.valueOf((Integer)record.get(property));
					name = name.toLowerCase();  
					if (name.startsWith(filter.toLowerCase())) {  
						return true;  
					}
				}
				return false;  
			}  
		};
		filterField.bind(grid.getStore());
		filterField.setProperty("organismName");
	}

	private SelectionListener<ButtonEvent> addRemoveButtonSelectionListener() {
		return new SelectionListener<ButtonEvent>() {
			@Override
			public void componentSelected(ButtonEvent ce) {
				Button removeButton = (Button)ce.getSource();
				String id = removeButton.getItemId().split("_")[1];
				BlastGrid blastGrid = MyPortal.map.get(id);
				Grid<BeanModel> gd = blastGrid.getGrid();
				List<BeanModel> models = gd.getSelectionModel().getSelectedItems();
				ListStore<BeanModel> gridStore = gd.getStore();
				for (BeanModel model : models)
				{
					gridStore.remove(model);
				}
				MyPortal.projectUnsaved = true;
			}
		};
	}

	private SelectionListener<ButtonEvent> addSaveButtonSelectionListener() {
		return new SelectionListener<ButtonEvent>() {
			@Override
			public void componentSelected(ButtonEvent ce) {
				Button saveButton = (Button)ce.getSource();
				String id = saveButton.getItemId().split("_")[1];
				BlastGrid blastGrid = MyPortal.map.get(id);
				Grid<BeanModel> gd = blastGrid.getGrid();
				List<BeanModel> models = gd.getStore().getModels();
				if (models.size() == 0)
				{
					Info.display("Error", "No data to save");
				}
				else
				{
					List<BlastResult> lst = fetchDataFromGrid(models);
					AsyncCallback<String> callback = new AsyncCallback<String>() {

						@Override
						public void onSuccess(String result) {
							Info.display("Result",result);
							MyPortal.projectUnsaved = false;
						}

						@Override
						public void onFailure(Throwable caught) {
							Info.display("Error", "Could not save project");
						}
					};
					MyBlast.blastServiceInterfaceAsync.saveProject(lst, callback);
				}
			}
		};
	}


	private GridCellRenderer<BeanModel> getRenderer()
	{
		GridCellRenderer<BeanModel> buttonRenderer = new GridCellRenderer<BeanModel>() {

			private boolean init;

			public Object render(final BeanModel model, final String property, ColumnData config, final int rowIndex,
					final int colIndex, ListStore<BeanModel> store, Grid<BeanModel> grid) {
				if (!init) {
					init = true;
					grid.addListener(Events.ColumnResize, new Listener<GridEvent<FormModel>>() {

						public void handleEvent(GridEvent<FormModel> be) {
							for (int i = 0; i < be.getGrid().getStore().getCount(); i++) {
								if (be.getGrid().getView().getWidget(i, be.getColIndex()) != null
										&& be.getGrid().getView().getWidget(i, be.getColIndex()) instanceof BoxComponent) {
									((BoxComponent) be.getGrid().getView().getWidget(i, be.getColIndex())).setWidth(be.getWidth() - 10);
								}
							}
						}
					});
				}
				Button b = new Button("Blast file", new SelectionListener<ButtonEvent>() {
					@Override
					public void componentSelected(ButtonEvent ce) {
						Button b = (Button) ce.getSource();
						final  String BlastfileName = b.getTitle();
						final String projectName = b.getData("projectName");
						AsyncCallback<String> callback = new AsyncCallback<String>() {

							@Override
							public void onFailure(Throwable caught) {
								Info.display("Error", "Could not fetch the Blast file");
							}	

							@Override
							public void onSuccess(String result) {

								Portlet portlet = new Portlet();
								portlet.setHeading(BlastfileName + ".out");
								MyPortal.configPanel(portlet);
								ContentPanel panel = new ContentPanel();
								panel.addText(result);
								panel.setHeaderVisible(false);
								Button button  = new Button("Download Me", new SelectionListener<ButtonEvent>() {

									@Override
									public void componentSelected(ButtonEvent ce) {
//										WidgetUtilities.addDownloadiframe(BlastfileName + ".out",MyPortal.txtField.getValue());
										WidgetUtilities.addDownloadiframe(BlastfileName + ".out",projectName);
									}});
								portlet.add(panel);
								portlet.setScrollMode(Scroll.AUTO);
								portlet.setHeight(340);
								ToolBar bar = new ToolBar();
								bar.add(button);
								portlet.setTopComponent(bar);
								MyPortal.portal.add(portlet, 1);
							}
						};
						String arg = projectName+"---"+b.getTitle();
						MyBlast.blastServiceInterfaceAsync.getData(arg, callback);
					}
				});
				b.setTitle(model.get("organismId").toString());
				b.setWidth(grid.getColumnModel().getColumnWidth(colIndex) - 10);
				b.setData("projectName", MyPortal.txtField.getValue());
				return b;
			}
		};
		return buttonRenderer;
	}

	private List<BlastResult> fetchDataFromGrid(List<BeanModel> models) {
		List<BlastResult> lst = new ArrayList<BlastResult>();
		for (BeanModel model : models)
		{
			BlastResult rslt = new BlastResult();
			rslt.setMaxEVal((Double)model.get("maxEVal")).
			setMinEVal((Double)model.get("minEVal")).
			setMinBitScore((Double)model.get("minBitScore")).
			setMaxBitScore((Double)model.get("maxBitScore")).
			setOrganismId((Integer)model.get("organismId")).
			setOrganismName((String)model.get("organismName"));
			lst.add(rslt);
		}
		BlastResult prgSettings = new BlastResult();
		lst.add(prgSettings.setProjectSettings(MyPortal.getProjectSettings(false))
				.setSelectedTaxes(MyPortal.getSelectedTaxes()).setOrganismName(MyPortal.txtField.getValue()));
		return lst;
	}

	public void download(boolean sync, List<BeanModel> models, AsyncCallback<String> callback, String ext)
	{
		List<BlastResult> lst = fetchDataFromGrid(models);
		BlastResult blastResult = lst.remove(lst.size() - 1);
		blastResult.setDoSyn(new Boolean(sync)).setFileExtension(ext);
		lst.add(blastResult);
		MyBlast.blastServiceInterfaceAsync.getFile(lst, callback);
	}
}
